package tron

import (
	"encoding/hex"
	"fmt"
	"log"
	"log/slog"
	"math/big"
	"raymond/util"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/fbsobreira/gotron-sdk/pkg/proto/api"
	"github.com/fbsobreira/gotron-sdk/pkg/proto/core"
	"github.com/mr-tron/base58"
)

var mapFunctionTcc20 = map[string]string{
	"a9059cbb": "transfer(address,uint256)",
	"70a08231": "balanceOf(address)",
}

type ListenTronResult struct {
	Result []TronTransaction
}

type TronTransaction struct {
	BlockNumber     string
	TimeStamp       string
	Hash            string
	BlockHash       string
	From            string
	ContractAddress string
	To              string
	Value           string
	InputData       string
	Confirmations   string
}

var msFormat string = "2006-01-02 15:04:05.000"
var startTimeFormatted string = "2024-04-29 09:50:00.000"
var nextBlockNumber int64 = 0
var maxBlockNumber int64 = 0

func ListenTRC20(listenContract string, listenAddr string) (*ListenTronResult, error) {
	c, err := NewClient(GetTrcApiRpcUrl())
	if err != nil {
		return nil, err
	}
	defer c.Close()

	var block *api.BlockExtention
	if nextBlockNumber == 0 {
		block, err = c.GRPC.GetNowBlock()
		if err != nil {
			slog.Error("GetNowBlock", err)
			return nil, err
		}
		nextBlockNumber = block.BlockHeader.RawData.Number
		nextTimestamp := block.BlockHeader.RawData.Timestamp / 1000
		maxBlockNumber = nextBlockNumber - 20

		startTime, err := time.ParseInLocation(msFormat, startTimeFormatted, time.UTC)
		if err != nil {
			slog.Error("parse time failed", err)
			return nil, err
		}
		startTimestamp := startTime.In(time.UTC).Unix()
		startTimestamp = startTimestamp - 20*3

		for nextTimestamp > startTimestamp {
			errorTimestamp := nextTimestamp - startTimestamp
			errorBlockNumber := errorTimestamp / 3
			if errorBlockNumber >= 20 && errorBlockNumber <= 40 {
				break
			}
			nextBlockNumber = nextBlockNumber - errorBlockNumber/2
			block, err = c.GRPC.GetBlockByNum(nextBlockNumber)
			if err != nil {
				slog.Error("GetBlockByNum", err)
				return nil, err
			}
			nextTimestamp = block.BlockHeader.GetRawData().Timestamp / 1000
		}
		if nextTimestamp < startTimestamp {
			nextBlockNumber += 20
		}
		nextBlockNumber = nextBlockNumber - 100
		block, err = c.GRPC.GetBlockByNum(nextBlockNumber)
		if err != nil {
			slog.Error("GetBlockByNum", err)
			return nil, err
		}
		nextBlockNumber = block.BlockHeader.RawData.Number
	} else {
		// 每次maxBlockNumber的增长都依赖间隔时间
		maxBlockNumber += 10
		nextBlockNumber = nextBlockNumber - 1
	}
	slog.Info("block number", "next", nextBlockNumber, "max", maxBlockNumber)

	var tronTxs []TronTransaction
	// 在定时任务的间隔时间内最多追90个块, 最多追到maxBlockNumber高度
	for i := 0; i < 90 && nextBlockNumber <= maxBlockNumber; i++ {
		block, err = c.GRPC.GetBlockByNum(nextBlockNumber + 1)
		if err != nil {
			slog.Error("GetBlockByNum", err)
			continue
		}

		nextBlockNumber = block.BlockHeader.RawData.Number
		blockHash := hex.EncodeToString(block.Blockid)
		timestamp := block.BlockHeader.RawData.Timestamp
		for _, txe := range block.Transactions {
			if txe.Result == nil || !txe.Result.Result {
				continue
			}
			hash := hex.EncodeToString(txe.Txid)
			tx := txe.GetTransaction()
			if tx == nil {
				continue
			}
			rets := tx.GetRet()
			if len(rets) < 1 {
				continue
			}
			contractRet := rets[0].GetContractRet()
			if contractRet != core.Transaction_Result_SUCCESS {
				continue
			}
			for _, contract := range tx.GetRawData().GetContract() {
				switch contract.Type {
				// https://github.com/smirkcat/tron-rpc/blob/main/trx/transcation.go
				case core.Transaction_Contract_TriggerSmartContract:
					// trc20转账
					tsc := &core.TriggerSmartContract{}
					err := contract.Parameter.UnmarshalTo(tsc)
					if err != nil {
						log.Println("parse contract param", err)
						continue
					}
					contractAddress := hex.EncodeToString(tsc.GetContractAddress())
					contractTronAddress := addressToBase58(contractAddress)
					if contractTronAddress == listenContract {
						//log.Println("data", hex.EncodeToString(tsc.GetData()))
						data := tsc.GetData()
						if len(data) >= 68 {
							if hex.EncodeToString(data[:4]) != "a9059cbb" {
								continue
							}
							// 多1位41
							data[15] = 65 // 0x41
							to := addressToBase58(hex.EncodeToString(data[15:36]))
							if to == listenAddr {
								from := addressToBase58(hex.EncodeToString(tsc.OwnerAddress))
								dataHex := hex.EncodeToString(data)
								amount := new(big.Int).SetBytes(common.TrimLeftZeroes(data[36:68])).Int64()
								tronTx := TronTransaction{
									BlockNumber:     fmt.Sprintf("%v", nextBlockNumber),
									TimeStamp:       fmt.Sprintf("%v", timestamp),
									Hash:            hash,
									BlockHash:       blockHash,
									From:            from,
									ContractAddress: contractTronAddress,
									To:              to,
									Value:           fmt.Sprintf("%v", amount),
									InputData:       dataHex,
									Confirmations:   core.Transaction_ResultContractResult_name[int32(contractRet)],
								}
								tronTxs = append(tronTxs, tronTx)
							}
						}
					}
				default:
				}
			}
		}
	}
	var resp ListenTronResult
	resp.Result = tronTxs
	return &resp, nil
}

func addressToBase58(address string) string {
	addb, _ := hex.DecodeString(address)
	hash1 := util.SHA256(util.SHA256(addb))
	secret := hash1[:4]
	for _, v := range secret {
		addb = append(addb, v)
	}
	return base58.Encode(addb)
}
